package com.example.logining.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.json.JSONUtil;
import com.example.logining.domain.dto.UserRegistrationDTO;
import com.example.logining.domain.pojo.SysUser;
import com.example.logining.domain.vo.LoginUser;
import com.example.logining.domain.vo.LoginUserVO;
import com.example.logining.mapper.UserMapper;
import com.example.logining.properties.JwtProperties;
import com.example.logining.service.UserService;
import com.example.logining.utils.JwtUtil;
import com.example.logining.utils.PasswordUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

/**
 * @title: UserServiceImpl
 * @author: wywang
 * @date: 2024/11/09
 * @description:
 **/
@Service
@Slf4j
public class UserServiceImpl implements UserService {

    @Autowired
    private UserMapper userMapper;
    @Autowired
    private AuthenticationManager authenticationManager;
    @Autowired
    private JwtProperties jwtProperties;
    @Autowired
    private StringRedisTemplate redisTemplate;

    public void registerUser(UserRegistrationDTO registrationUser) {

        log.info("注册用户: " + registrationUser.getUsername());
        SysUser user = BeanUtil.copyProperties(registrationUser, SysUser.class);
        String username = user.getUsername();
        String rawPassword = user.getPassword();
        //1.现根据用户名查询数据库，看用户是否存在
        SysUser existingUser = userMapper.findByUsername(username);
        if (existingUser != null) {
            throw new IllegalArgumentException("Username already exists.");
        }

        String encodedPassword = PasswordUtils.encodePassword(rawPassword);
        user.setPassword(encodedPassword);
        userMapper.insertUser(user);
    }

    public LoginUserVO login(String username, String rawPassword) {

        // 1.AuthenticationManager authenticate 进行身份验证
        UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username, rawPassword);
        Authentication authenticate;
        try {
            authenticate = authenticationManager.authenticate(authenticationToken);
        } catch (AuthenticationException e) {
            // 记录日志时使用占位符代替密码
            log.error("登录失败: " + username + " with password: ********");

            // 返回友好的错误信息
            throw new RuntimeException("登陆失败,账号密码错误", e);
        }

        if(Objects.isNull(authenticate)){
            throw new RuntimeException("登陆失败,账号密码错误");
        }
        LoginUser loginUser = (LoginUser) authenticate.getPrincipal();

        // 2.把用户信息存到redis中

        String userId = loginUser.getUser().getId().toString();
        redisTemplate.opsForValue().set("login:"+userId, JSONUtil.toJsonStr(loginUser));

        Map<String,Object> claims = new HashMap<>();
        claims.put("userId",userId);
        String token = JwtUtil.createJWT(jwtProperties.getUserSecretKey(), jwtProperties.getUserTtl(), claims);


        return new LoginUserVO().builder()
                .userId(loginUser.getUser().getId())
                .username(loginUser.getUser().getUsername())
                .token(token)
                .build();
    }

    /**
     * 退出时清空redis中的用户信息，
     * 下次返回进入Filter以后，先判断token，然后再判断redis中的用户信息是否存在，
     * 如果不存在则重新登陆
     */
    @Override
    public void logout() {
        Authentication authentication =
                SecurityContextHolder.getContext().getAuthentication();
        LoginUser loginUser = (LoginUser) authentication.getPrincipal();
        String userId = loginUser.getUser().getId().toString();
        redisTemplate.opsForHash().delete("login:"+userId);
    }
}
